class_name HoldGunBase
extends BaseHoldItem

@export var show_laser: bool = true
@onready var muzzle: Marker2D = $Muzzle
@onready var laser_line: Line2D = $LaserParent/LaserLine
@onready var laser_dot: Node2D = $LaserParent/LaserDot
@onready var laser_light: PointLight2D = $LaserParent/Light/LaserLight
@onready var laser_light_parent: Node2D = $LaserParent/Light
@onready var laser_ray: RayCast2D = $LaserParent/LaserRay
@onready var laser_parent: Node2D = $LaserParent

# if muzzle in shape
@onready var muzzle_in_shape_tester: Area2D = $MuzzleInShapeTester

# shoot
@export var shoot_line_duration: float = 0.1
@onready var shoot_line: Line2D = $ShootLine
@export var shoot_line_start_width: float = 6
@onready var particle_muzzle_flash: GPUParticles2D = $ParticleMuzzleFlash
@onready var particle_gun_shoot_hit_effect: GPUParticles2D = $ParticleGunShootHitEffect

var current_shoot_hit_target: Node2D
var current_shoot_hit_distance_2_muzzle: float = 0 
var current_shoot_hit_point: Vector2 = Vector2.ZERO

# shoot ray
@onready var shoot_ray_parent: Node2D = $ShootRayParent
var shoot_ray_arr: Array[RayCast2D] = []


func _ready() -> void:
	set_show_laser(show_laser)
	init_shoot_ray()
	
	set_show_ui_bullet_info()
	
	
func set_show_ui_bullet_info():
	var player: Player = holder as Player
	var gun_config = item_data.item_config as ItemGunBaseConfig
	player.level.ui_bullet_remain_info.visible = true
	update_bullet_count()
	

func update_bullet_count():
	var player: Player = holder as Player
	var gun_config = item_data.item_config as ItemGunBaseConfig
	var bullet_count = player.player_data.get_bullet_count(gun_config.bullet_type)
	GlobalSignals.bullet_count_changed.emit(bullet_count)
	

func set_hide_ui_bullet_info():
	var player: Player = holder as Player
	player.level.ui_bullet_remain_info.visible = false
	

func init_shoot_ray():
	for index in range(shoot_ray_parent.get_child_count()):
		shoot_ray_arr.append(shoot_ray_parent.get_child(index)) 


func set_show_laser(b: bool):
	show_laser = b
	if show_laser:
		laser_parent.modulate.a = 1.0
		laser_light.enabled = true
	else:
		laser_parent.modulate.a = 0.0
		laser_light.enabled = false


func _physics_process(delta: float) -> void:
	update_laser()


func is_muzzle_in_shape():
	return muzzle_in_shape_tester.get_overlapping_bodies().size() > 0


func update_laser():
	if not show_laser: return
	
	var gun_config = item_data.item_config as ItemGunBaseConfig
	var player: Player = holder as Player
	
	laser_ray.position = muzzle.position        
	laser_ray.target_position = Vector2.RIGHT * gun_config.shoot_max_distance 
	
	if is_muzzle_in_shape():
		laser_dot.visible = false
		laser_line.visible = false
		laser_light.visible = false
	else:
		laser_line.visible = true
		laser_light.visible = true
		
		if laser_ray.is_colliding():
			var hit_global_pos = laser_ray.get_collision_point()
			var distance = hit_global_pos.distance_to(muzzle.global_position)
			laser_line.points = [muzzle.position, muzzle.position + Vector2.RIGHT * distance]
			laser_dot.visible = true
			laser_dot.position = muzzle.position + Vector2.RIGHT * distance
			
			laser_light.scale.x = distance / 16.0
			laser_light_parent.position = muzzle.position + Vector2.RIGHT * distance / 2.0
		else:
			laser_line.points = [muzzle.position, muzzle.position + Vector2.RIGHT * gun_config.shoot_max_distance]
			laser_dot.visible = false
			
			laser_light.scale.x = gun_config.shoot_max_distance / 16.0
			laser_light_parent.position = muzzle.position + Vector2.RIGHT * gun_config.shoot_max_distance / 2.0


func update_shoot_hit_info_by_ray_test():
	var gun_config = item_data.item_config as ItemGunBaseConfig
	
	current_shoot_hit_target = null
	current_shoot_hit_distance_2_muzzle = 0
	
	for shoot_ray in shoot_ray_arr:
		shoot_ray.target_position = shoot_ray.target_position.normalized() * gun_config.shoot_max_distance
		
		if shoot_ray.is_colliding():
			current_shoot_hit_target = shoot_ray.get_collider() as Node2D
			current_shoot_hit_distance_2_muzzle = shoot_ray.get_collision_point().distance_to(muzzle.global_position)
			current_shoot_hit_point = shoot_ray.get_collision_point()
			break


func shoot():
	if is_muzzle_in_shape(): return
	
	var player: Player = holder as Player
	var gun_config = item_data.item_config as ItemGunBaseConfig
	#var remain_bullet_count = player.player_data.get_bullet_count(gun_config.bullet_type)
	#if remain_bullet_count <= 0:
		#return
		
	#player.player_data.reduce_bullet(gun_config.bullet_type)
	#update_bullet_count()
	
	update_shoot_hit_info_by_ray_test()
	
	player.level.camera.start_shake(gun_config.attack_cam_shake_intensity, gun_config.attack_cam_shake_duration)
	
	shoot_line.visible = true
	if current_shoot_hit_target:
		shoot_line.points = [muzzle.position, muzzle.position + Vector2.RIGHT * current_shoot_hit_distance_2_muzzle]
	else:
		shoot_line.points = [muzzle.position, muzzle.position + Vector2.RIGHT * gun_config.shoot_max_distance]
	shoot_line.width = shoot_line_start_width
	
	var tween = create_tween()
	tween.tween_property(shoot_line, "width", 0, shoot_line_duration) \
	.set_trans(Tween.TRANS_SINE) \
	.set_ease(Tween.EASE_OUT)
	
	var material = particle_muzzle_flash.process_material as ParticleProcessMaterial
	particle_muzzle_flash.shoot()
	AudioManager.play_sound("sfx_gun_shoot")
	
	
	if current_shoot_hit_target:
		particle_gun_shoot_hit_effect.global_position = current_shoot_hit_point
		particle_gun_shoot_hit_effect.shoot()
		player.level.camera.start_shake(gun_config.attack_cam_shake_intensity, gun_config.attack_cam_shake_duration)
		
		var hit_target: Node2D = current_shoot_hit_target as Node2D
		if hit_target is Area2D:
			hit_target = hit_target.source
			
		if hit_target.has_method("take_hit"):
			# give attack
			var critical_rate = gun_config.critical_rate
			var is_critical = (randf() <= critical_rate)
			var attack = gun_config.attack + player.player_data.get_attack()
			var critical_attack = attack * (1.0 + gun_config.critical_bonus)
			
			var hit_data = HitData.new()
			hit_data.is_critical = is_critical
			hit_data.attack = attack if not is_critical else critical_attack
			hit_data.knockback = gun_config.knockback
			
			hit_target.take_hit(hit_data, holder)
		
